package leapmotion;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.io.IOException;

import javax.swing.JComponent;
import javax.swing.JFrame;

import com.leapmotion.leap.CircleGesture;
import com.leapmotion.leap.Controller;
import com.leapmotion.leap.FingerList;
import com.leapmotion.leap.Frame;
import com.leapmotion.leap.Gesture;
import com.leapmotion.leap.Gesture.State;
import com.leapmotion.leap.GestureList;
import com.leapmotion.leap.Hand;
import com.leapmotion.leap.KeyTapGesture;
import com.leapmotion.leap.Listener;
import com.leapmotion.leap.ScreenTapGesture;
import com.leapmotion.leap.SwipeGesture;
import com.leapmotion.leap.Vector;

public class LeapMotion {

  public static void main(String[] args) {

    new LeapMotion();

  }

  private MyCanvas myCanvas;

  public LeapMotion() {
    JFrame window = new JFrame();
    window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    window.setBounds(30, 30, 500, 500);
    myCanvas = new MyCanvas();
    window.getContentPane().add(myCanvas);
    window.setVisible(true);

    //	testRotating(3600);

    init();

  }

  private void init() {
    try {
      Thread.sleep(1000);
    } catch (Exception e) {
      e.printStackTrace();
    }

    // Create a sample listener and controller
//    System.load("/Users/Quan/working/eclipse/Phd_Leap-Motion/libs/libLeap.dylib");
//    System.load("/Users/Quan/working/eclipse/Phd_Leap-Motion/libs/libLeapCSharp.dylib");
//    System.load("/Users/Quan/working/eclipse/Phd_Leap-Motion/libs/libLeapJava.dylib");
    SampleListener listener = new SampleListener(myCanvas);
    Controller controller = new Controller();

    // Have the sample listener receive events from the controller
    controller.addListener(listener);
    
    System.out.println("Press Enter to exit...");
    try {
      System.in.read();
    } catch (IOException e) {
      e.printStackTrace();
    }

//     Remove the sample listener when done
    controller.removeListener(listener);
  }

}

class MyCanvas extends JComponent {
  private static final long serialVersionUID = 507181590378305030L;
  Rectangle rectangle;
  double rotAngle = 0;
  public MyCanvas() {
    super();
    rectangle = new Rectangle(100, 100, 100, 100);
  }

  public void paint(Graphics g) {
    super.paint(g);
    Graphics2D g2d = (Graphics2D) g;
    g2d.setColor(new Color(150, 150, 150));
    System.out.println("Rotate rectangle " + rotAngle + " degrees...");

    g2d.rotate(Math.toRadians(rotAngle), rectangle.x + rectangle.width / 2.0, rectangle.y + rectangle.height / 2);
    g.fillRect(rectangle.x, rectangle.y, rectangle.width, rectangle.height);

  }

  public void setRotationAngle(double angle) {
    rotAngle += angle;
  }
}

class SampleListener extends Listener {

  private MyCanvas myCanvas;
  
  private double angle;
  private double lastAngle = 0;

  public SampleListener(MyCanvas myCanvas) {
    this.myCanvas = myCanvas;
  }

  private double stableAngle = 5; // 30;
  private Vector palmVector = null;

//  private boolean isRorating = false; // might help to deal with "dark area"
//
//  private Vector thumbAtStartingPoint = null;
  
  public void onInit(Controller controller) {
    System.out.println("Initialized");
  }

  public void onConnect(Controller controller) {
    System.out.println("Connected");
    controller.enableGesture(Gesture.Type.TYPE_SWIPE);
    controller.enableGesture(Gesture.Type.TYPE_CIRCLE);
    controller.enableGesture(Gesture.Type.TYPE_SCREEN_TAP);
    controller.enableGesture(Gesture.Type.TYPE_KEY_TAP);
  }

  public void onDisconnect(Controller controller) {
    // Note: not dispatched when running in a debugger.
    System.out.println("Disconnected");
  }

  public void onExit(Controller controller) {
    System.out.println("Exited");
  }

  public float computeLength(Vector vector) {
    return (float) Math.sqrt(vector.getX() * vector.getX() + vector.getY() * vector.getY() + vector.getZ() * vector.getZ());
  }

  /**
   * 
   * @param v1
   * @param v2
   * @return
   */
  public float getAngle(Vector v1, Vector v2) {
    float dotProduct = v1.getX() * v2.getX() + v1.getY() * v2.getY() + v1.getZ() * v2.getZ();
    // return (float)Math.acos(dotProduct / (computeLength(v1) *
    // computeLength(v2)));
    return (float) Math.acos(dotProduct / (computeLength(v1) * computeLength(v2))) * 180 / (float) Math.PI;
  }

  public void onFrame(Controller controller) {
    // Get the most recent frame and report some basic information
    Frame frame = controller.frame();
    if (!frame.hands().isEmpty()) {
      // Get the first hand
      Hand hand = frame.hands().get(0);
      // Check if the hand has any fingers
      FingerList fingers = hand.fingers();
      if(fingers.count() > 0) {
//        System.out.println("Do some computation here.....");
        if (palmVector == null) {
          // init
          palmVector = hand.palmNormal();
        } else {
          // check if rotating or not
          // if the index finger is stable && the thumb
          // moves
//          System.out.println("palm: " + hand.palmPosition() + "\t" + getAngle(hand.palmPosition(), hand.palmNormal()));
          angle = getAngle(palmVector, hand.palmNormal());
          if(Math.abs(lastAngle - angle) > stableAngle) {
            // just print out
            System.out.println("Already rotates " + angle + " degrees...");
            if(hand.palmNormal().getX() >= 0) {
              System.out.println("> 0");
              myCanvas.rotAngle = -angle;
            }
            else {
              System.out.println("< 0");
              myCanvas.rotAngle = angle;
            }
            // rotate the rectangle
            lastAngle = angle;
            myCanvas.repaint();
          }
        }

//        // Get the hand's sphere radius and palm position
//        System.out.println("Hand sphere radius: " + hand.sphereRadius() + " mm, palm position: " + hand.palmNormal() + ", first palm: " + palmVector);
//
//        // Get the hand's normal vector and direction
//        Vector normal = hand.palmNormal();
//        Vector direction = hand.direction();
//
//        // Calculate the hand's pitch, roll, and yaw angles
//        System.out.println("Hand pitch: " + Math.toDegrees(direction.pitch()) + " degrees, " + "roll: " + Math.toDegrees(normal.roll()) + " degrees, "
//            + "yaw: " + Math.toDegrees(direction.yaw()) + " degrees");
      }
      else {
        // no finger is detected...
      }
    }
    else {
      // TODO: need to decide when to restart tracing,
      // which means to end of a rotation???
    }

    GestureList gestures = frame.gestures();
    for (int i = 0; i < gestures.count(); i++) {
      Gesture gesture = gestures.get(i);

      switch (gesture.type()) {
        case TYPE_CIRCLE:
          CircleGesture circle = new CircleGesture(gesture);

          // Calculate clock direction using the angle between circle
          // normal and pointable
          String clockwiseness;
          if (circle.pointable().direction().angleTo(circle.normal()) <= Math.PI / 4) {
            // Clockwise if angle is less than 90 degrees
            clockwiseness = "clockwise";
          } else {
            clockwiseness = "counterclockwise";
          }

          // Calculate angle swept since last frame
          double sweptAngle = 0;
          if (circle.state() != State.STATE_START) {
            CircleGesture previousUpdate = new CircleGesture(controller.frame(1).gesture(circle.id()));
            sweptAngle = (circle.progress() - previousUpdate.progress()) * 2 * Math.PI;
          }

          System.out.println("Circle id: " + circle.id() + ", " + circle.state() + ", progress: " + circle.progress() + ", radius: "
              + circle.radius() + ", angle: " + Math.toDegrees(sweptAngle) + ", " + clockwiseness);
          break;
        case TYPE_SWIPE:
          SwipeGesture swipe = new SwipeGesture(gesture);
          System.out.println("Swipe id: " + swipe.id() + ", " + swipe.state() + ", position: " + swipe.position() + ", direction: "
              + swipe.direction() + ", speed: " + swipe.speed());
          break;
        case TYPE_SCREEN_TAP:
          ScreenTapGesture screenTap = new ScreenTapGesture(gesture);
          System.out.println("Screen Tap id: " + screenTap.id() + ", " + screenTap.state() + ", position: " + screenTap.position() + ", direction: "
              + screenTap.direction());
          break;
        case TYPE_KEY_TAP:
          KeyTapGesture keyTap = new KeyTapGesture(gesture);
          System.out.println("Key Tap id: " + keyTap.id() + ", " + keyTap.state() + ", position: " + keyTap.position() + ", direction: "
              + keyTap.direction());
          break;
        default:
          System.out.println("Unknown gesture type.");
          break;
      }
    }

    if (!frame.hands().isEmpty() || !gestures.isEmpty()) {
//      System.out.println();
    }
  }
}
